package com.gne.data.platform.mysocket;

import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Component;

import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

/**
 * @author Gjing
 *         <p>
 *         netty服务端处理器
 **/
@Slf4j
@Component
@ChannelHandler.Sharable
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
	/**
	 * 客户端连接会触发
	 */
	@Resource
	private MongoTemplate mongoTemplate;
	public static NettyServerHandler handler;

	@PostConstruct
	public void init() {
		handler = this;
	}

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		log.info("Channel active......");
	}

	Map<String, List<byte[]>> map = new HashMap<>();

	List<String> commands = new ArrayList<>();

	// 断包使用
	String currentCommand;

	/**
	 * 客户端发消息会触发
	 */
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
		try {
			do {
				// System.out.println(handler.mongoTemplate);
				// log.info("服务器收到消息: {}", msg.toString());
				String host = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress();
				// 这东西肯定是不一样的
				int port = ((InetSocketAddress) ctx.channel().remoteAddress()).getPort();

				// 都是byte的消息
				byte[] data = (byte[]) msg;
				int packageType = packageType(data);
				if (packageType == -1) {
					// TODO 是错包，记录下来，这种包肯定是有问题的
				}
				int cmd = -1;
				if (packageType == 0) {
					// TODO 全包并且经过检验,下面的似乎不用做了
				}

				String uuid = UUID.randomUUID().toString().replace("-", "");
				int command = hasCommand(packageType, data);
				if (command > -1) {
					currentCommand = command + "-" + uuid;
				}
				List<byte[]> list = null;
				if ("".equals(currentCommand)) {
					// TODO 错误处理，记录在数据库，elk
				} else
					list = map.get(currentCommand);
				int listInit = 0;
				if (list == null) {
					listInit = 1;
					list = new ArrayList<>();
				}
				list.add(data);// 数据接上去
				if (1 == listInit)
					map.put(currentCommand, list);
				int smallPackageComplete = 0;
				if (packageType == 2) {
					smallPackageComplete = smallPackageComplete();
					log.info("校验开始");
				}
				if (1 == smallPackageComplete) {
					log.info("失败");
				}

				switch (command) {
				case 1:

					sendDataCmd8001(ctx);
					break;
				}
				ctx.flush();
				// --------------TODO 到底要不要
				// ctx.disconnect();
				// 打扫工作
				if (packageType == 2) {// 尾包
					// 接好了，就判断数据是否完整
					currentCommand = "";
				}
			} while (false);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	void parserData0001(byte[] data) {
		// 11-14版本号
		byte[] v = new byte[4];
		v[0] = data[11];
		v[1] = data[12];
		v[2] = data[13];
		v[3] = data[14];
		String version = bytesToString16(v);
		// sn号 15-18
		byte[] sn = new byte[4];
		sn[0] = data[15];
		sn[1] = data[16];
		sn[2] = data[17];
		sn[3] = data[18];
		String snStr = bytesToString16(sn);

		// 写入数据库
	}

	public static String bytesToString16(byte[] b) {
		char[] _16 = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < b.length; i++) {
			sb.append(_16[b[i] >> 4 & 0xf]).append(_16[b[i] & 0xf]);
		}
		return sb.toString();
	}

	void sendDataCmd8001(ChannelHandlerContext ctx) {
		byte[] res = new byte[512];
		res[0] = (byte) 0x80;
		res[1] = 1;
		// 成功还是失败或者是有命令要处理
		res[2] = 0;// TODO 是否有命令要处理
		String head = "serverip:port=";
		byte[] headByteArr = head.getBytes();
		for (int i = 0; i < headByteArr.length; i++) {
			res[3 + i] = headByteArr[i];
		}
		// 17开始用
		String ip = "121.40.83.184:8080," + "121.40.83.184:12334," + "platform.gnetek.com:8080,"
				+ "platform.gnetek.com:12334" + '\0';
		byte[] ipBytes = ip.getBytes();
		if (ipBytes.length + 17 > 512) {
			// ip地址有点长
			res = Arrays.copyOf(ipBytes, 1024);
		}
		for (int i = 0; i < ipBytes.length; i++) {
			res[17 + i] = ipBytes[i];
		}
		ctx.write(res);
	}

	static final byte tail_flag = (byte) 0xff;
	static final byte head_flag = (byte) 0xaa;
	static final int d_high = 2;
	static final int d_low = 3;

	/**
	 * 包头0xAAAA（2字节） <br/>
	 * 整个数据包长度(2字节)(包括包头和长度本身) <br/>
	 * 包序号（4个字节） <br/>
	 * 设备类型（1个字节） <br/>
	 * 命令码（2字节）<br/>
	 * 数据域（长度不定，根据具体命令而定） <br/>
	 * 包尾0xFFFF(2字节)<br/>
	 * 
	 * @return 0 没问题<br/>
	 *         1 开头不是AAAA，说明是续包 <br/>
	 *         1 包不完整，就是尾部ffff，四个f不对 <br/>
	 *         2 包长和本身长度不符合<br/>
	 */
	int smallPackageComplete() {
		// // 判断包是否完整
		// int dataLength = data.length;
		// if (
		// // head
		// head_flag == data[0] && head_flag == data[1] &&
		// // tail
		// tail_flag == data[dataLength - 1] && tail_flag == data[dataLength - 2]) {
		// int standardLength = (data[d_high] & 0xff) * 256
		// // 高位+低位
		// + (data[d_low] & 0xff);
		// if (standardLength != dataLength) {
		// return 2;
		// }
		// log.info("standardLength:{},dataLength:{}", standardLength, dataLength);
		// } else
		// return 1;
		// return 0;
		// version----2
		List<byte[]> list = map.get(currentCommand);
		byte[] data = list.get(0);
		int dataLength = 0;
		int standardLength = (data[d_high] & 0xff) * 256
				// 高位+低位
				+ (data[d_low] & 0xff);
		for (int i = 0; i < list.size(); i++) {
			byte[] b = list.get(i);
			dataLength += b.length;
		}
		if (standardLength != dataLength) {
			return 1;
		}
		return 0;
	}

	/**
	 * 
	 * @param data
	 * @return 全包，断包，错包（可能就是断包）
	 */
	int packageType(byte[] data) {
		// FIXME 这种判断方式很有问题
		int dataLength = data.length;
		if (head_flag == data[0] && head_flag == data[1]) {
			if (tail_flag == data[dataLength - 1] && tail_flag == data[dataLength - 2]) {
				int standardLength = (data[d_high] & 0xff) * 256
						// 高位+低位
						+ (data[d_low] & 0xff);
				if (standardLength != dataLength) {// 包好像是全的，就是数据长度不对
					return -1;
				}
				return 0;// 全包
			}
			return 1;// 头包
		}
		if (tail_flag == data[dataLength - 1] && tail_flag == data[dataLength - 2]) {

			return 2;// 尾包
		}
		return 3;// 中间包
	}

	int hasCommand(int type, byte[] data) {
		if (1 == type) {
			if (data.length < 11)
				return -2;
			int command = (data[9] & 0xff) * 256 + (data[10] & 0xff);
			return command;
		}
		return -1;
	}

	/**
	 * 发生异常触发
	 */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
		log.info("error.....");
		cause.printStackTrace();
		ctx.close();
	}
}